import struct


def swap16(value):
    """交换16位整数的字节顺序"""
    return ((value & 0xFF) << 8) | ((value >> 8) & 0xFF)


def rgb888_to_rgb565(r, g, b):
    """RGB888转RGB565"""
    RGB565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
    return swap16(RGB565)


def bmp_rle16_to_c_array(bmp_path, array_name, out_path, column_first=False):
    """ 读取位BMP文件 """
    with open(bmp_path, 'rb') as f_bmp:
        # 读取BMP头
        header = f_bmp.read(54)
        width = struct.unpack('<I', header[18:22])[0]
        height = struct.unpack('<I', header[22:26])[0]
        bpp = struct.unpack('<H', header[28:30])[0]
        if bpp != 24:
            raise ValueError("仅支持24位BMP")
        offset = struct.unpack('<I', header[10:14])[0]
        f_bmp.seek(offset)
        row_padded = ((width * 3 + 3) & ~3)

        # 读取所有像素（BMP底到顶，需反序）
        rows = []
        for _ in range(height):
            data = f_bmp.read(row_padded)
            row = []
            for i in range(0, width * 3, 3):
                b, g, r = data[i], data[i+1], data[i+2]
                row.append(rgb888_to_rgb565(r, g, b))
            rows.append(row)

        if not column_first:
            pixels = [pix for row in reversed(rows) for pix in row]  # 按行优先
        else:
            # 转置
            columns = [[row[i] for row in reversed(rows)]
                       for i in range(len(rows[0]))]
            pixels = [pix for col in columns for pix in col]  # 按列优先

    """，使用RLE16压缩并输出C语言格式数组。"""

    n = len(pixels)
    with open(out_path, 'w') as f_out:
        f_out.write("/**")
        f_out.write("\nRLE16压缩数据格式说明：")
        f_out.write("\n只有游程长度大于2时才压缩，否则为literal（0x0xxx,像素...）。")
        f_out.write("\n- 只有游程长度大于2时才压缩为run（高位1，低15位为长度，后跟像素值）;")
        f_out.write("\n- 其它情况为literal（高位0，低15位为长度，后跟若干像素值）;")
        f_out.write("\n- 其中xxx为长度，像素为RGB565格式。")
        f_out.write(f"\n    Width: {width}, Height: {height}")
        f_out.write("\n*/\n")
        f_out.write(f"\nconst unsigned short {array_name}[] = {{")

        rle_count = 0
        line_count = 0
        i = 0
        while i < n:
            run_val = pixels[i]
            run_len = 1

            # 只有run_len>2才压缩为run
            while i + run_len < n and pixels[i + run_len] == run_val and run_len < 0x7FFF:
                run_len += 1

            if run_len > 2:
                # 输出重复(run)
                f_out.write(
                    f"\n /* RLE:{run_len:5d} @ {(i%width):3d},{(i//width):3d} */"
                    f" 0x8000U + {run_len}, 0x{run_val:04X}, ")
                rle_count += 2
                line_count += 2
                i += run_len
            else:
                # 输出原始(literal)
                lit_start = i
                lit_len = 1
                while (lit_start + lit_len < n):
                    next_run_val = pixels[lit_start + lit_len]
                    next_run_len = 1
                    while (lit_start + lit_len + next_run_len < n and
                           pixels[lit_start + lit_len + next_run_len] == next_run_val and next_run_len < 0x7FFF):
                        next_run_len += 1
                    # 如果后面有长度大于2的run，则结束literal
                    if next_run_len > 2:
                        break
                    lit_len += 1
                f_out.write(
                    f"\n /* ABS:{lit_len:5d} @ {(i%width):3d},{(i//width):3d} */ {lit_len:5d} , ")
                for j in range(lit_len):
                    f_out.write(f"0x{pixels[lit_start + j]:04X}, ")
                rle_count += 1 + lit_len
                line_count += 1 + lit_len
                i += lit_len
        f_out.write("\n};\n")
        f_out.write(
            f"// RLE16 length: {rle_count}, total {rle_count*2} bytes for {n} pixels. \n")
        f_out.write("// EOF //\n")
    print(f"C数组已生成到{out_path}")


if __name__ == "__main__":
    bmp_file = "LOGO-480.bmp"  # 修改为你的BMP文件路径
    array_name = "image_rle16"
    out_file = "output.c"

    bmp_rle16_to_c_array(bmp_file, array_name, out_file, column_first=True)
